import { BodyItemConstant } from '../constants/InputItemConstants';
import CommonConstants from '../constants/CommonConstants';
import CommonUtils from './CommonUtils';

const { BODY_WEIGHT, SMOKING_HABIT, ALCOHOL_HABIT, ALCOHOL_FREQUENCY, ALCOHOL_AMOUNT_BEER,
  EXERCISE_TIME_SITTING, EXERCISE_TIME_STANDING, EXERCISE_TIME_LABORING } = BodyItemConstant;
const AVERISK05 = 0.5;  // 平均リスク（0.5）
const AVERISK09 = 0.9;  // 平均リスク（0.9）
const AVERISK14 = 1.4;  // 平均リスク（1.4）
const AVERISK19 = 1.9;  // 平均リスク（1.9）
const AVERISK27 = 2.7;  // 平均リスク（2.7）
const AVERISK30 = 3.0;  // 平均リスク（3.0）

// 平均リスクと比較した結果のメッセージ
const COMPARE_WITH_AVE = {
  HIGHER: '大腸がんになる危険度は平均より高いですね。生活習慣の見直しが必要です。',
  EQUAL: '大腸がんになる危険度は平均と同じで、特に心配は要らないと言えます。加齢とともに増える大腸がん。これからも大腸がんにならないための生活の工夫を続けましょう。',
  LOWER: '大腸がんになる危険度は平均より低く、特に心配は要らないと言えます。加齢とともに増える大腸がん。これからも大腸がんにならないための生活の工夫を続けましょう。',
}

class ColorectalCancerLogic {

  /**
   * リスク計算
   * @param {number} age 年齢
   * @param {number} smokingHabit 喫煙習慣
   * @param {number} alcoholHabit 飲酒習慣
   * @param {number} alcoholFrequency 飲酒頻度
   * @param {number} bmi BMI
   * @param {number} mets 身体活動量
   * @param {number} ethanol 飲酒量
   * @return {Object.<Object>} がんになる危険度（自分、平均、最大、最小）
   */
  static calcRisk({ age, smokingHabit, alcoholHabit, alcoholFrequency }, { bmi, mets, ethanol }) {
    age = CommonUtils.convertAge(age);// 69歳を超えた場合、69歳とみなす
    let ageVal = 0.08 * (age - 52.9);
    let bmiVal = 0.047 * (CommonUtils.getFloatFormat(bmi, 1) - 23.4);
    let metsVal = 0.019 * (mets - 28.7);

    // 飲酒カテゴリー取得
    let AlcoholCategoryArr = this._getAlcoholCategory(alcoholHabit, alcoholFrequency, ethanol);
    let alcoholCategory1Val = 0.163 * (AlcoholCategoryArr[0] - 0.235);
    let alcoholCategory2Val = 0 * (AlcoholCategoryArr[1] - 0.077);
    let alcoholCategory3Val = 0.358 * (AlcoholCategoryArr[2] - 0.481);
    let alcoholCategory4Val = 0.659 * (AlcoholCategoryArr[3] - 0.208);

    // 喫煙カテゴリー取得
    let SmokingCategoryArr = this._getSmokingCategory(smokingHabit);
    let smokingCategory1Val = 0 * (SmokingCategoryArr[0] - 0.236);
    let smokingCategory2Val = 0.071 * (SmokingCategoryArr[1] - 0.239);
    let smokingCategory3Val = 0.239 * (SmokingCategoryArr[2] - 0.525);

    // あなたのリスク算出
    let powerVal = Math.exp(ageVal + bmiVal - metsVal - alcoholCategory1Val + alcoholCategory2Val +
      alcoholCategory3Val + alcoholCategory4Val + smokingCategory1Val + smokingCategory2Val + smokingCategory3Val);
    let riskVal = 1 - Math.pow(0.9882, powerVal);
    // 平均リスク算出
    let aveValArr = [AVERISK05, AVERISK09, AVERISK14, AVERISK19, AVERISK27, AVERISK30];
    let aveVal = Math.floor((age - 40) / 5);
    let aveRiskVal = aveValArr[aveVal];
    // ＭＡＸリスク算出
    let maxRiskVal = this._getMaxMinRisk(ageVal, 39, 23.05, 0.484591);
    // ＭＩＮリスク算出
    let minRiskVal = this._getMaxMinRisk(ageVal, 15, 46.7, -0.576409);

    // probability:あなたのリスク、平均リスク
    const results = {
      probability: {
        you: (riskVal * 1000) / 10,
        ave: aveRiskVal,
        max: (maxRiskVal * 1000) / 10,
        min: (minRiskVal * 1000) / 10,
      },
      diagnosis: {
        messageCompWithAve: this._getMessageCompWithAve(aveRiskVal, ((riskVal * 1000) / 10)),
      },
    };
    return results;
  }

  /**
   * アドバイス生成
   * @param {number} smokingHabit 喫煙習慣
   * @param {number} alcoholHabit 飲酒習慣
   * @param {number} alcoholFrequency 飲酒頻度
   * @param {number} bmi BMI
   * @param {number} mets 身体活動量
   * @param {number} ethanol 飲酒量
   * @return {Object.<Object>} アドバイス（飲酒、喫煙、BMI、身体活動量）
   */
  static takeAdvices({ smokingHabit, alcoholHabit, alcoholFrequency }, { bmi, mets, ethanol }) {
    // 飲酒カテゴリー取得
    let AlcoholCategoryArr = this._getAlcoholCategory(alcoholHabit, alcoholFrequency, ethanol);
    let alcoholAdvices;
    // 飲酒
    if (AlcoholCategoryArr[0] === 1) {
      alcoholAdvices = '引き続きお酒を飲まないようにしましょう。';
    } else if (AlcoholCategoryArr[1] === 1) {
      alcoholAdvices = 'ときどき飲む程度ならさほど影響はありませんが、週１回以上など、習慣化しないようにしましょう。';
    } else if (AlcoholCategoryArr[2] === 1) {
      alcoholAdvices = '現在の飲酒量でも大腸がんのリスクを上げてしまいます。飲む量を減らすか、回数を減らす努力をしましょう。';
    } else {
      alcoholAdvices = '飲酒量は多い方ですね。まずは飲む量を減らす工夫をしましょう。';
    }
    // 喫煙
    let SmokingCategoryArr = this._getSmokingCategory(smokingHabit);
    let smokingAdvices;
    if (SmokingCategoryArr[0] === 1 || SmokingCategoryArr[1] === 1) {
      smokingAdvices = '引き続きたばこを吸わないようにしましょう。';
    } else {
      smokingAdvices = 'たばこを吸わないようにしましょう。';
    }
    // BMI
    let bmiAdvices;
    if (CommonUtils.getFloatFormat(bmi, 1) < 25) {
      bmiAdvices = 'BMIは適正な範囲に入っていますね。この調子で引き続きがんばりましょう。';
    } else {
      bmiAdvices = 'BMIは適正な範囲から外れていますね。無理のない範囲での体重管理が必要です。';
    }
    // 身体活動量
    let metsAdvices;
    if (mets < 24.7) {
      metsAdvices = '身体活動量は非常に少ないです。持病など、特別の理由がない場合はシミュレーションを参考に、無理のない範囲で活動量を増やしてみてください。';
    } else {
      metsAdvices = '身体活動量を増やすとさらに大腸がんのリスクが下がるかもしれません。シミュレーション画面で体験してみてください。';
    }

    // advices:アドバイス
    const results = {
      alcoholAdvices,
      smokingAdvices,
      bmiAdvices,
      metsAdvices,
    };
    return results;
  }

  /**
   * 飲酒カテゴリー取得
   * @param {number} alcoholHabit 飲酒習慣
   * @param {number} alcoholFrequency 飲酒頻度
   * @param {number} ethanol 飲酒量
   * @return {number[]} 飲酒カテゴリー
   */
  static _getAlcoholCategory(alcoholHabit, alcoholFrequency, ethanol) {
    let alcoholCategory1 = 0;
    let alcoholCategory2 = 0;
    let alcoholCategory3 = 0;
    let alcoholCategory4 = 0;
    if ((alcoholHabit === ALCOHOL_HABIT.IS_DRINKING.value && alcoholFrequency === ALCOHOL_FREQUENCY.MONTH_LESS1.value) ||
      alcoholHabit === ALCOHOL_HABIT.NO_DRINKING.value || alcoholHabit === ALCOHOL_HABIT.STOPPED.value) {
      alcoholCategory1 = 1;
    } else if (alcoholFrequency === ALCOHOL_FREQUENCY.MONTH_LESS3.value) {
      alcoholCategory2 = 1;
    } else {
      if (ethanol < 300) {
        alcoholCategory3 = 1;
      } else {
        alcoholCategory4 = 1;
      }
    }
    return [alcoholCategory1, alcoholCategory2, alcoholCategory3, alcoholCategory4];
  }

  /**
   * 喫煙カテゴリー取得
   * @param {number} smokingHabit 喫煙習慣
   * @return {number[]} 喫煙カテゴリー
   */
  static _getSmokingCategory(smokingHabit) {
    let smokingCategory1 = 0;
    let smokingCategory2 = 0;
    let smokingCategory3 = 0;
    if (smokingHabit === SMOKING_HABIT.NO_SMOKING.value) {
      smokingCategory1 = 1;
    } else if (smokingHabit === SMOKING_HABIT.STOPPED_SHORT_AGO.value || smokingHabit === SMOKING_HABIT.STOPPED_LONG_AGO.value) {
      smokingCategory2 = 1;
    } else {
      smokingCategory3 = 1;
    }
    return [smokingCategory1, smokingCategory2, smokingCategory3];
  }

  /**
   * ＭＡＸ、ＭＩＮリスク算出
   * @param {number} ageVal 年齢算出値
   * @param {number} bmi BMI固定値
   * @param {number} mets 身体活動量固定値
   * @param {number} category 飲酒、喫煙カテゴリ固定値
   * @return {number} ＭＡＸ、ＭＩＮリスク
   */
  static _getMaxMinRisk(ageVal, bmi, mets, category) {
    return 1 - Math.pow(0.9882, (Math.exp(ageVal + 0.047 * (bmi - 23.4) - (0.019 * (mets - 28.7)) + category)));
  }

  /**
   * 健康なグループと不健康なグループの項目ごとのラベルと値のセットを返す
   */
  static getHealthy({ bodyHeight }) {
    const SMOKING_LABEL = '喫煙';
    const ALCOHOL_LABEL = '飲酒';
    const ALCOHOL_HABIT_SAMPLE = '飲酒習慣例)';
    const EXERCISE_LABEL = '運動';
    const EXERCISE_HABIT_SAMPLE = '運動習慣例)';
    const EXERCISE_TIME_SITTING_LABEL = '座っている時間';
    const EXERCISE_TIME_STANDING_LABEL = '立ったり歩いたり<br />している時間';
    const EXERCISE_TIME_LABORING_LABEL = '肉体労働やスポ<br />ーツをする時間';

    const { goodWeightRange, badWeightRange } = this._getWeightRange(bodyHeight);
    const maxMets = CommonUtils.calcMets(EXERCISE_TIME_SITTING.LONG.value,
      EXERCISE_TIME_STANDING.LONG.value, EXERCISE_TIME_LABORING.LONG.value);
    const minMets = CommonUtils.calcMets(EXERCISE_TIME_SITTING.SHORT.value,
      EXERCISE_TIME_STANDING.SHORT.value, EXERCISE_TIME_LABORING.NONE.value);

    return {
      good: [
        {
          label: BODY_WEIGHT.label,
          value: `${goodWeightRange.minWeight.toFixed(CommonConstants.DECIMAL_DIGIT)}kg～${goodWeightRange.maxWeight.toFixed(CommonConstants.DECIMAL_DIGIT)}kg`,
          isSample: false,
        },
        { label: SMOKING_LABEL, value: SMOKING_HABIT.NO_SMOKING.label, isSample: false },
        { label: ALCOHOL_LABEL, value: ALCOHOL_HABIT.NO_DRINKING.label, isSample: false },
        { label: ALCOHOL_HABIT_SAMPLE, value: ALCOHOL_FREQUENCY.MONTH_LESS1.label, isSample: true },
        { label: EXERCISE_LABEL, value: `身体活動量が${maxMets}メッツ`, isSample: false },
        {
          label: EXERCISE_HABIT_SAMPLE,
          value: [
            { label: EXERCISE_TIME_SITTING_LABEL, value: EXERCISE_TIME_SITTING.LONG.label },
            { label: EXERCISE_TIME_STANDING_LABEL, value: EXERCISE_TIME_STANDING.LONG.label },
            { label: EXERCISE_TIME_LABORING_LABEL, value: EXERCISE_TIME_LABORING.LONG.label },
          ],
          isSample: true,
        },
      ],
      bad: [
        {
          label: BODY_WEIGHT.label,
          value: `${badWeightRange.minWeight.toFixed(CommonConstants.DECIMAL_DIGIT)}kg～${badWeightRange.maxWeight.toFixed(CommonConstants.DECIMAL_DIGIT)}kg`,
          isSample: false,
        },
        { label: SMOKING_LABEL, value: SMOKING_HABIT.IS_SMOKING.label, isSample: false },
        { label: ALCOHOL_LABEL, value: '週あたりエタノール300g以上', isSample: false },
        { label: ALCOHOL_HABIT_SAMPLE, value: `${ALCOHOL_FREQUENCY.EVERY_DAY.label}ビールで大瓶${ALCOHOL_AMOUNT_BEER.EQUAL2.label}以上`, isSample: true },
        { label: EXERCISE_LABEL, value: `身体活動量が${minMets}メッツ`, isSample: false },
        {
          label: EXERCISE_HABIT_SAMPLE,
          value: [
            { label: EXERCISE_TIME_SITTING_LABEL, value: EXERCISE_TIME_SITTING.SHORT.label },
            { label: EXERCISE_TIME_STANDING_LABEL, value: EXERCISE_TIME_STANDING.SHORT.label },
            { label: EXERCISE_TIME_LABORING_LABEL, value: EXERCISE_TIME_LABORING.NONE.label },
          ],
          isSample: true,
        },
      ],
    };
  }

  /**
   * 身長から健康なグループの体重範囲と不健康なグループの体重範囲を取得
   * @param {string|number} bodyHeight 身長（cm）
   * @return {number} BMI
   */
  static _getWeightRange(bodyHeight) {
    const GOOD_BMI = 15.0;
    const BAD_BMI = 39.0;
    const BMI_RANGE = 0.9;
    return {
      goodWeightRange: {
        minWeight: CommonUtils.calcWeightFromBMI(bodyHeight, GOOD_BMI),
        maxWeight: CommonUtils.calcWeightFromBMI(bodyHeight, GOOD_BMI + BMI_RANGE),
      },
      badWeightRange: {
        minWeight: CommonUtils.calcWeightFromBMI(bodyHeight, BAD_BMI),
        maxWeight: CommonUtils.calcWeightFromBMI(bodyHeight, BAD_BMI + BMI_RANGE),
      },
    };
  }

  /**
   * 平均と比較した結果のメッセージを返す
   * @param {*} ave 
   * @param {*} risk 
   */
  static _getMessageCompWithAve(ave, risk) {
    let result = '';
    if (ave < risk.toFixed(CommonConstants.DECIMAL_DIGIT)) {
      result = COMPARE_WITH_AVE.HIGHER;
    } else if (ave === risk.toFixed(CommonConstants.DECIMAL_DIGIT)) {
      result = COMPARE_WITH_AVE.EQUAL;
    } else if (ave > risk.toFixed(CommonConstants.DECIMAL_DIGIT)) {
      result = COMPARE_WITH_AVE.LOWER;
    }
    return result;
  }
}

export default ColorectalCancerLogic
